/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.alibaba.citrus.maven.eclipse.base.eclipse.writers.rad;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Arrays;
import java.util.Comparator;

import com.alibaba.citrus.maven.eclipse.base.eclipse.Constants;
import com.alibaba.citrus.maven.eclipse.base.eclipse.EclipseSourceDir;
import com.alibaba.citrus.maven.eclipse.base.eclipse.Messages;
import com.alibaba.citrus.maven.eclipse.base.eclipse.writers.AbstractEclipseWriter;
import com.alibaba.citrus.maven.eclipse.base.eclipse.writers.wtp.AbstractWtpResourceWriter;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
import org.codehaus.plexus.util.xml.XMLWriter;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.codehaus.plexus.util.xml.Xpp3DomWriter;

/**
 * Adapts the .classpath file for RAD6 for now write hardcoded: target/websphere/classes future releases could make this
 * varriable.
 *
 * @author <a href="mailto:nir@cfc.at">Richard van Nieuwenhoven </a>
 */
public class RadEjbClasspathWriter
        extends AbstractEclipseWriter {

    private static final String CLASSPATH = "classpath";

    private static final String CLASSPATH_FILE = ".classpath";

    private static final String CLASSPATHENTRY = "classpathentry";

    private static final String CON = "con";

    private static final String KIND = "kind";

    private static final String LIB = "lib";

    private static final String OUTPUT = "output";

    private static final String PATH = "path";

    private static final String SRC = "src";

    private static final String TARGET_WEBSPHERE_CLASSES = "target/websphere/generated-classes";

    private static final String VAR = "var";

    private static final String WEBSPHERE6CONTAINER =
            "com.ibm.wtp.server.java.core.container/com.ibm.ws.ast.st.runtime.core.runtimeTarget.v60/was.base.v6";

    /**
     * write the .classpath file to the project root directory.
     *
     * @param sourceDirs           all eclipse source directorys
     * @param localRepository      the local reposetory
     * @param buildOutputDirectory build output directory (target)
     * @throws MojoExecutionException when writing the config files was not possible
     * @see AbstractWtpResourceWriter#write(EclipseSourceDir[], ArtifactRepository, File)
     */
    public void write()
            throws MojoExecutionException {
        String packaging = config.getPackaging();
        if (Constants.PROJECT_PACKAGING_EJB.equalsIgnoreCase(packaging)) {
            new File(config.getEclipseProjectDirectory(), TARGET_WEBSPHERE_CLASSES).mkdirs();
            File classpathFile = new File(config.getEclipseProjectDirectory(), CLASSPATH_FILE);

            if (!classpathFile.exists()) {
                return;
            }
            Xpp3Dom classpath = readXMLFile(classpathFile);
            Xpp3Dom[] children = classpath.getChildren();
            for (int index = 0; index < children.length; index++) {
                if (LIB.equals(children[index].getAttribute(KIND))
                    && TARGET_WEBSPHERE_CLASSES.equals(children[index].getAttribute("path"))) {
                    return; // nothing to do!
                }
            }

            Xpp3Dom newEntry = new Xpp3Dom(CLASSPATHENTRY);
            newEntry.setAttribute(KIND, LIB);
            newEntry.setAttribute(PATH, TARGET_WEBSPHERE_CLASSES);
            classpath.addChild(newEntry);

            newEntry = new Xpp3Dom(CLASSPATHENTRY);
            newEntry.setAttribute(KIND, CON);
            newEntry.setAttribute(PATH, WEBSPHERE6CONTAINER);
            classpath.addChild(newEntry);

            children = classpath.getChildren();
            for (int index = children.length - 1; index >= 0; index--) {
                if (children[index].getValue() == null) {
                    children[index].setValue("");
                }
            }

            removeDupicateWAS6Libs(classpath);
            classpath = orderClasspath(classpath);

            Writer w;
            try {
                w = new OutputStreamWriter(new FileOutputStream(classpathFile), "UTF-8");
            } catch (IOException ex) {
                throw new MojoExecutionException(Messages.getString("EclipsePlugin.erroropeningfile"),
                                                 ex); //$NON-NLS-1$
            }
            XMLWriter writer = new PrettyPrintXMLWriter(w, "UTF-8", null);
            Xpp3DomWriter.write(writer, classpath);
            IOUtil.close(w);
        }
    }

    /**
     * determinate of witch type this classpath entry is. this is used for sorting them.
     *
     * @param classpathentry the classpath entry to sort
     * @return an integer identifieing the type
     * @see RadEjbClasspathWriter#orderClasspath(Xpp3Dom)
     */
    private int detectClasspathEntryType(Xpp3Dom classpathentry) {
        String kind = classpathentry.getAttribute(KIND);
        String path = classpathentry.getAttribute(PATH);

        if (kind == null || path == null) {
            return 6;
        }

        boolean absolutePath = path.startsWith("\\") || path.startsWith("/");
        boolean windowsAbsolutePath = path.indexOf(':') >= 0;
        boolean anyAbsolutePath = absolutePath || windowsAbsolutePath;

        if (kind.equals(SRC) && !absolutePath) {
            return 1;
        } else if (kind.equals(LIB) && !anyAbsolutePath) {
            return 2;
        } else if (kind.equals(SRC)) {
            return 3;
        } else if (kind.equals(VAR)) {
            return 4;
        } else if (kind.equals(LIB)) {
            return 5;
        } else if (kind.equals(OUTPUT)) {
            return 7;
        } else {
            return 6;
        }
    }

    /**
     * Order of classpath this is nessesary for the ejb's the generated classes are elsewise not found. 1 - kind=src
     * ohne starting '/' oder '\' 2 - kind=lib kein ':' und kein start mit '/' oder '\' 3 - kind=src mit ohne starting
     * '/' oder '\' 4 - kind=var 5 - kind=lib ein ':' oder start mit '/' oder '\' 6 - rest 7 - kind=output
     *
     * @param classpath the classpath to sort
     * @return dom-tree representing ordered classpath
     */
    private Xpp3Dom orderClasspath(Xpp3Dom classpath) {
        Xpp3Dom[] children = classpath.getChildren();
        Arrays.sort(children, new Comparator() {
            public int compare(Object o1, Object o2) {
                return detectClasspathEntryType((Xpp3Dom) o1) - detectClasspathEntryType((Xpp3Dom) o2);
            }
        });
        Xpp3Dom resultClasspath = new Xpp3Dom(CLASSPATH);
        for (int index = 0; index < children.length; index++) {
            resultClasspath.addChild(children[index]);
        }
        return resultClasspath;
    }

    /**
     * read an xml file (application.xml or .modulemaps).
     *
     * @param xmlFile an xmlfile
     * @return dom-tree representing the file contents
     */
    private Xpp3Dom readXMLFile(File xmlFile) {
        try {
            Reader reader = new InputStreamReader(new FileInputStream(xmlFile), "UTF-8");
            Xpp3Dom applicationXmlDom = Xpp3DomBuilder.build(reader);
            return applicationXmlDom;
        } catch (FileNotFoundException e) {
            return null;
        } catch (Exception e) {
            log.error(Messages.getString("EclipsePlugin.cantreadfile", xmlFile.getAbsolutePath()));
            // this will trigger creating a new file
            return null;
        }
    }

    /**
     * Losche alle pfade die nach was6 zeigen diese sind erkennbar an den parrent runtimes/base_v6/lib.
     *
     * @param classpath classpath to remove was6 libraries
     */
    private void removeDupicateWAS6Libs(Xpp3Dom classpath) {
        Xpp3Dom[] children;
        children = classpath.getChildren();
        for (int index = children.length - 1; index >= 0; index--) {
            try {
                File path = new File(children[index].getAttribute(PATH));

                if (path.exists() && path.getParentFile().getName().equals(LIB)
                    && path.getParentFile().getParentFile().getName().equals("base_v6")
                    && path.getParentFile().getParentFile().getParentFile().getName().equals("runtimes")) {
                    Xpp3Dom[] currentChildren = classpath.getChildren();
                    for (int deleteIndex = currentChildren.length - 1; deleteIndex >= 0; deleteIndex--) {
                        if (currentChildren[deleteIndex] == children[index]) {
                            classpath.removeChild(deleteIndex);
                            break;
                        }
                    }
                }
            } catch (Exception e) {
                log.debug(e);
            }
        }
    }
}
